
#include <string.h>
//#include "type_a.h"
#include "contactless.h"
#include "WCI.h"
#include "CardDefine.h"
#include "ISO14443Command.h"
#include "ISO14443p3a.h"

/*************************************************************/
//2014.03.06修改版
/*************************************************************/



/************************************************************************/
/*
*/
/************************************************************************/

static unsigned char TypeA_BCC(unsigned char b[], unsigned int len) {
	unsigned int i;
	unsigned char bcc = 0;

	for (i = 0; i < len; i++) {
		bcc ^= b[i];
	}
	return bcc;
}

/************************************************************************/
/*
*/
/************************************************************************/

static int TypeA_Probe(WCI* wci, unsigned char cmd, unsigned char ATQA[2]) {
	unsigned int rbits = 0;
	unsigned int rmax = 2;
	unsigned int rlen = 0;
	int result;

	WCI_Config(wci, WCI_Cfg_Short);
	result = WCI_ExchangeBit(wci, &cmd, sizeof(cmd), 7, 
			ATQA, &rlen, &rbits, 
			rmax, WaitTime_Ms(1, 0), FGT_A);
	if (result != 0) {
		return result;
	}

	if (rlen != 2) {
		return CmdS_Exception;
	}
	return result;

}

/****************************************************************************************/
/*名称：TypeA_Anticollision								*/
/*功能：TypeA_Anticollision卡片防冲突							*/
/*输入：selcode =0x93，0x95，0x97							*/
/*       			    			     				*/
/*	       								 		*/
/*输出：										*/
/*	       	pSnr[0],pSnr[1],pSnr[2],pSnr[3]pSnr[4] =UID                            	*/
/*       	OK: 应答正确                                                            */
/*	 		ERROR: 应答错误							*/
/****************************************************************************************/
static int TypeA_Anti(WCI* wci, unsigned int loop, unsigned char sel, unsigned char uid[], unsigned int* ulen) {
	int result;
	unsigned char tx[8];
	unsigned char rx[8];
	unsigned int rlen;
	unsigned int tlen;
	unsigned int i;
	unsigned char* puid = &tx[2];
	unsigned char bytes;
	unsigned char bits;
	unsigned int tbits = 16;
	unsigned int c;
	unsigned int rbits;
//		unsigned int rmbits = 5 * 8;
	unsigned int rmax = 5;

	memset(tx, '\0', sizeof(tx));
	tx[0] = sel;
	WCI_Config(wci, WCI_Cfg_Anti);
	for (i = 0; i < loop; i++) {
		bits = (unsigned char)tbits & 7;
		bytes = (unsigned char)tbits / 8;
		tx[1] = (unsigned char)bytes << 4 | bits;
		rbits = bits;
		tlen = bytes;
		if(bits){
			tlen++;
		}
		result = WCI_ExchangeBit(wci, 
			tx, tlen, bits, 
			rx, &rlen, &rbits, rmax, 
			WaitTime_Ms(1, 0), FGT_A);
		
		if (result == WCI_S_Timeout) {
			return result;
		}
		if (bytes + rlen > 7) {
			return CmdS_Exception;
		}
		for (c = 0; c < rlen; c++) {
			tx[bytes + c] |= rx[c];
		}
		
		if(rbits > 0){
			rlen--;
		}
		tbits += rlen * 8 + rbits;
		tbits -= bits;
		if (result == WCI_S_OK) {
			break;
		}
		if (tbits >= 56) {
			break;
		}
		tbits++; // select the uid that coll bit is 0。 选择碰撞位为0的UID
	}

	if (i == loop) {
		return CmdS_OverRun;
	}

	if ((tbits / 8 != 7) || (tbits & 0x07)) {
		return CmdS_Invalid;
	}

	if (TypeA_BCC(puid, 5) != 0) {
		return CmdS_Invalid;
	}

	memcpy(uid, puid, 4);
	*ulen = 4;
	return result;
}

/****************************************************************************************/
/*名称：TypeA_Select									*/
/*功能：TypeA_Select卡片选卡								*/
/*输入：selcode =0x93，0x95，0x97							*/
/*      pSnr[0],pSnr[1],pSnr[2],pSnr[3]pSnr[4] =UID 			    		*/
/*	       								 		*/
/*输出：										*/
/*	       	pSak[0],pSak[1],pSak[2] =SAK			                        */
/*       	OK: 应答正确                                                            */
/*	 		ERROR: 应答错误							*/
/****************************************************************************************/

static int TypeA_Select(WCI* wci, unsigned char sel, unsigned char uid[], unsigned int ulen, unsigned char *sak) {
	int result;
	unsigned char tx[8];
	unsigned int tlen = 0;
	unsigned int rlen = 0;
	unsigned int rmax = 1;

	tx[tlen++] = sel;
	tx[tlen++] = 0x70;
	memcpy(&tx[tlen], uid, ulen);
	tlen += ulen;
	tx[tlen++] = TypeA_BCC(uid, ulen);

	WCI_Config(wci, WCI_Cfg_Sel);
	result = WCI_Exchange(wci, tx, tlen, sak, &rlen, rmax, WaitTime_Ms(1, 0), FGT_A);
	return result;
}
/****************************************************************************************/
/*名称：TypeA_Halt																		*/
/*功能：TypeA_Halt卡片停止																*/
/*输入：																				*/
/*       			    			     												*/
/*	       								 												*/
/*输出：																			 	*/
/*	       											                            		*/
/*       	OK: 应答正确                                                              	*/
/*	 		ERROR: 应答错误																*/
/****************************************************************************************/

#if 0
static int TypeA_Halt(WCI* wci, unsigned char cid) {
	int result;
	unsigned char tx[8];
	unsigned int tlen = 0;
	unsigned char rx[2];
	unsigned int rlen = 0;
	unsigned int rmax = 0; // ignore

	tx[tlen++] = 0x50;
	tx[tlen++] = cid;

	WCI_Config(wci, WCI_Cfg_HaltA);
	result = WCI_Exchange(wci, tx, tlen, rx, &rlen, rmax, WaitTime_Ms(1, 0), FGT_A);
	return result;
}
#endif 

static int ISO14443A_CheckATQA(unsigned char atqa[2]) {
  /*
    多卡激活时，有冲突导致ATQA错误的可能，因此忽略ATQA检查
  */
	return 1;
}

void ISO14443A_SetMode(WCI* wci) {
	WCI_SetMode(wci, WCI_Mode_A);
}

int ISO14443A_RequestA(WCI* wci, unsigned char atqa[2]) {
	return TypeA_Probe(wci, 0x26, atqa);
}

int ISO14443A_WakeupA(WCI* wci, unsigned char atqa[2]) {
	return TypeA_Probe(wci, 0x52, atqa);
}

static int ISO14443A_ANTI(WCI* wci, unsigned char selCode, unsigned char uid[], unsigned int* ulen) {
	return TypeA_Anti(wci, 32, selCode, uid, ulen);
}

static int ISO14443A_Select(WCI* wci, unsigned char selCode, unsigned char uid[], unsigned int ulen, unsigned char* sak) {
	return TypeA_Select(wci, selCode, uid, ulen, sak);
}

static int ISO14443A_CheckUID(unsigned char uid[], unsigned int ulen) {
	unsigned int i;

	if (ulen > 12) {
		return 0;
	}
	for (i = 0; i < ulen; i += 4) {
		if (ulen > (i + 4) && uid[i] != 0x88) {
			return 0;
		}
	}

	return 1;
}

static int ISO14443A_AntiCheckUID(unsigned char uid0, unsigned char cascade, unsigned char level) {
#if 0
	//EMV 检测到CT不为0x88立即报错
	if (cascade != level) {
		if (uid0 != 0x88) {
			return 0;
		}
	}
	else {
		if (uid0 == 0x88) {
			return 0;
		}
	}
	return 1;
#else
	return 1;
#endif
}

int ISO14443A_AntiLoop(WCI* wci, unsigned char cascade, unsigned char uid[], unsigned char* ulen, unsigned char *sak) {
	static const unsigned char sel[3] = { 0x93, 0x95, 0x97 };
	unsigned char level;
	unsigned char* puid = uid;
	unsigned int rlen;
	int ret;

	*ulen = 0;	
	for (level = 0; level < 3; level++) {
		ret = ISO14443A_ANTI(wci, sel[level], puid, &rlen);
		if (ret) {
			return ret;
		}
		if (!ISO14443A_AntiCheckUID(puid[0], cascade, level)){
			return CmdS_PortocolError;
		}

		ret = ISO14443A_Select(wci, sel[level], puid, rlen, sak);
		if (ret) {
			return ret;
		}

		*ulen += (unsigned char)rlen;
		if (*sak & 0x04) { //uid not complete
			puid += rlen;
		}
		else
		if (*sak & 0x20) { // uid complete
			return ret;
		}
		else { //uid not compliant
			return CmdS_NotCompliant;
		}
	}
	return CmdS_OverRun;
}

int ISO14443A_Halt(WCI* wci, unsigned char cid) {
	int result;
	unsigned char tx[8];
	unsigned int tlen = 0;
	unsigned char rx[2];
	unsigned int rlen = 0;
	unsigned int rmax = 0; // ignore

	tx[tlen++] = 0x50;
	tx[tlen++] = cid;

	WCI_Config(wci, WCI_Cfg_HaltA);
	result = WCI_Exchange(wci, tx, tlen, rx, &rlen, rmax, WaitTime_Ms(1, 0), FGT_A);
	return result;
}
static int ISO14443A_DoDetect(WCI* wci, uint8_t req, uint8_t atqa[2]){
	int ret;
	int i;
	uint8_t code = (req == PICC_Op_Wakeup ? 0x52 : 0x26);
	
	for(i = 0; i < 2; i++){
		ret = TypeA_Probe(wci, code, atqa);
		if ((ret == WCI_S_OK) || (ret == WCI_S_Coll)) {
			break;
		}
		WCI_Delay_Fc(WaitTime_Ms(6, 0));
	}
	return ret;
}

int ISO14443A_Activate(WCI* wci, unsigned char req, ISO14443AInfo_T* info) {
	unsigned char cascade;
	int ret;

	ISO14443A_SetMode(wci);
	//detect
	ret = ISO14443A_DoDetect(wci, req, info->atqa);
	if (!((ret == WCI_S_OK) || (ret == WCI_S_Coll))) {
		return -1;
	}

	if (!ISO14443A_CheckATQA(info->atqa)) {
		return CmdS_Invalid;
	}
	cascade = info->atqa[0] >> 6;
	// anti
	ret = ISO14443A_AntiLoop(wci, cascade, info->uid, &info->ulen, &info->sak);
	if (ret && (ret != CmdS_NotCompliant)) {
		return ret;
	}

	if (!ISO14443A_CheckUID(info->uid, info->ulen)) {
		return CmdS_PortocolError;
	}
	
	return ret;
}

